//! 时钟设备接口实现
#![feature(lazy_cell)]
#![allow(unsafe_code)]
#![allow(clippy::linkedlist)]
#![allow(clippy::cast_precision_loss)]

use std::{
    collections::LinkedList,
    ops::Div,
    sync::{
        atomic::{AtomicU64, Ordering},
        Arc, LazyLock,
    },
};

use muldiv::MulDiv;
use number_prefix::NumberPrefix;
use objectid::ObjectId;
use rcu::RcuLock;
use trace::HwClockTrace;

mod trace;

/// 时钟周期单位
///
/// 时钟存储一个以 2^-32 ns 为单位表示时钟周期的值
///
/// 它可以表示:
/// * 周期从 2^-32 ns 到 4s
/// * 频率从 ~0.25Hz 到 2e10Ghz
///
/// 频率表示的精度随着频率的增加而降低:
/// * 在 100Mhz 时, 精度是 ~2mHz
/// * 在 1Ghz 时, 精度是 ~0.2Hz
/// * 在 10Ghz 时, 精度是 ~20Hz
const CLOCK_PERIOD_1SEC: u64 = 1000000000u64 << 32;

/// 从 ns 转换为时钟周期
const fn clock_period_from_ns(ns: u64) -> u64 {
    ns * (CLOCK_PERIOD_1SEC / 1000000000u64)
}

/// 从 hz 转换为时钟周期
const fn clock_period_from_hz(hz: u64) -> u64 {
    if hz == 0 {
        return 0;
    }
    CLOCK_PERIOD_1SEC / hz
}

/// 将周期转换为 hz
const fn clock_period_to_hz(period: u64) -> u64 {
    if period == 0 {
        return 0;
    }
    CLOCK_PERIOD_1SEC / period
}

/// 时钟事件
#[derive(Debug)]
pub enum ClkEvent {
    /// 时钟周期正在更新
    ClockUpdate = 1,
    /// 时钟周期即将更新
    ClockPreUpdate = 2,
}

/// 时钟事件实现
pub trait ClkEventImpl: ObjectId {
    /// 通知时钟周期更新
    fn notify_clock_upadte(&self, event: ClkEvent);
}

/// 硬件时钟定义
pub struct HwClock {
    // 时钟节点名字
    name: String,

    // 时钟周期
    clk_period: Arc<HwClockInput>,

    // 子时钟节点计算乘数因子和除数因子
    multiplier: AtomicU64,
    divider: AtomicU64,

    // 子时钟节点链表
    childs: RcuLock<LinkedList<Arc<HwClock>>>,

    // 时钟事件对象
    clk_event: Arc<dyn ClkEventImpl>,
}

unsafe impl Send for HwClock {}
unsafe impl Sync for HwClock {}

impl HwClock {
    #[inline]
    fn clock_get_child_period(&self) -> u64 {
        let period = self.clk_period.clock_get();
        period
            .mul_div_floor(
                self.multiplier.load(Ordering::Relaxed),
                self.divider.load(Ordering::Relaxed),
            )
            .unwrap()
    }

    fn clock_propagate_period(&self, notify: bool) {
        let child_period = self.clock_get_child_period();
        let childs = self.childs.read().unwrap();
        childs.iter().for_each(|child| {
            if child.clk_period.clock_get() != child_period {
                if notify {
                    child.clk_event.notify_clock_upadte(ClkEvent::ClockPreUpdate);
                }
                child.clk_period.set_period(child_period);
                HwClockTrace::trace_clock_update(
                    &child.name,
                    &self.name,
                    &child.clk_period.clock_get(),
                    &notify,
                );
                if notify {
                    child.clk_event.notify_clock_upadte(ClkEvent::ClockUpdate);
                }
                child.clock_propagate_period(notify);
            }
        });
    }

    /// 广播时钟
    pub fn clock_propagate(&self) {
        HwClockTrace::trace_clock_propagate(&self.name);
        self.clock_propagate_period(true);
    }

    /// 显示时钟频率
    pub fn clock_display_freq(&self) -> String {
        let hz = self.clk_period.clock_get_hz();
        match NumberPrefix::decimal(hz as f64) {
            NumberPrefix::Standalone(hz) => {
                format!("{}Hz", hz)
            },
            NumberPrefix::Prefixed(prefix, n) => {
                format!("{:0.3}{}Hz", n, prefix)
            },
        }
    }

    /// 设置时钟节点时钟周期
    pub fn clock_set(&self, period: u64) -> bool {
        if self.clk_period.clock_get() == period {
            return false;
        }
        HwClockTrace::trace_clock_set(
            &self.name,
            &self.clk_period.clock_get_hz(),
            &clock_period_to_hz(period),
        );
        self.clk_period.set_period(period);
        true
    }

    /// 设置时钟节点时钟周期(hz)
    #[inline]
    pub fn clock_set_hz(&self, hz: u64) -> bool {
        self.clock_set(clock_period_from_hz(hz))
    }

    /// 设置时钟节点时钟周期(ns)
    #[inline]
    pub fn clock_set_ns(&self, ns: u64) -> bool {
        self.clock_set(clock_period_from_ns(ns))
    }

    /// 设置时钟节点分频因数
    ///
    /// # Panics
    /// 除数`divider`为零将会 panic
    pub fn clock_set_mul_div(&self, multiplier: u64, divider: u64, propagate: bool) {
        assert!(divider != 0);
        HwClockTrace::trace_clock_set_mul_div(
            &self.name,
            &self.multiplier.load(Ordering::Relaxed),
            &multiplier,
            &self.divider.load(Ordering::Relaxed),
            &divider,
        );
        self.multiplier.store(multiplier, Ordering::Relaxed);
        self.divider.store(divider, Ordering::Relaxed);

        if propagate {
            self.clock_propagate();
        }
    }

    /// 更新时钟节点时钟周期
    #[inline]
    pub fn clock_update(&self, period: u64) {
        if self.clock_set(period) {
            self.clock_propagate();
        }
    }

    /// 更新时钟节点时钟周期(hz)
    #[inline]
    pub fn clock_update_hz(&self, hz: u64) {
        self.clock_update(clock_period_from_hz(hz));
    }

    /// 更新时钟节点时钟周期(ns)
    #[inline]
    pub fn clock_update_ns(&self, ns: u64) {
        self.clock_update(clock_period_from_ns(ns));
    }

    /// 时钟 ticks 转换为 ns
    #[inline]
    pub fn clock_ticks_to_ns(&self, ticks: u64) -> u64 {
        let value = ticks.checked_mul(self.clk_period.clock_get());
        if let Some(v) = value { v >> 32 } else { u64::MAX }
    }

    /// 时钟 ns 转换为 ticks
    #[inline]
    pub fn clock_ns_to_ticks(&self, ns: u64) -> u64 {
        let period = self.clk_period.clock_get();
        if period == 0 {
            return 0;
        }
        (ns << 32).div(period)
    }

    /// 时钟节点是否激活
    #[inline]
    pub fn clock_is_enabled(&self) -> bool {
        self.clk_period.clock_get() != 0
    }

    /// 时钟节点设置一个孩子节点
    #[allow(clippy::missing_panics_doc)]
    pub fn clock_set_child(&self, child: Arc<HwClock>) {
        HwClockTrace::trace_clock_set_source(&child.name, &self.name);
        child.clk_period.set_period(self.clock_get_child_period());
        {
            let mut childs = self.childs.write().unwrap();
            childs.push_back(child.clone());
        }
        child.clock_propagate_period(false);
    }
}

/// 时钟节点构建器
pub struct HwClockBuilder {
    multiplier: u64,
    divider: u64,
    clk_input: Option<Arc<HwClockInput>>,
    clk_event: Option<Arc<dyn ClkEventImpl>>,
}

impl HwClockBuilder {
    /// 创建一个构建器
    pub fn builder() -> Self {
        Self { multiplier: 1, divider: 1, clk_input: None, clk_event: None }
    }

    /// 设置分频因数
    pub fn set_mul_div(mut self, multiplier: u64, divider: u64) -> Self {
        self.multiplier = multiplier;
        self.divider = divider;
        self
    }

    /// 设置一个时钟输入
    pub fn clock_input(mut self, clk_input: Arc<HwClockInput>) -> Self {
        self.clk_input = Some(clk_input);
        self
    }

    /// 设置一个时钟事件通知
    pub fn clock_event(mut self, clk_event: Arc<dyn ClkEventImpl>) -> Self {
        self.clk_event = Some(clk_event);
        self
    }

    /// 构建一个`HwClock`
    ///
    /// # Panics
    /// 如果配置不符合预期将会 panic
    pub fn build(self) -> HwClock {
        HwClock {
            name: self.clk_event.as_ref().unwrap().id().to_string(),
            clk_period: self.clk_input.unwrap(),
            multiplier: AtomicU64::new(self.multiplier),
            divider: AtomicU64::new(self.divider),
            childs: RcuLock::new(LinkedList::new()),
            clk_event: self.clk_event.unwrap(),
        }
    }
}

/// `ClockInput` 时钟获取实现
#[derive(Debug, Default)]
pub struct HwClockInput {
    // 当前节点时钟周期
    period: AtomicU64,
}

impl HwClockInput {
    /// 获取时钟
    #[inline]
    pub fn clock_get(&self) -> u64 {
        self.period.load(Ordering::Relaxed)
    }

    /// 获取时钟 hz
    #[inline]
    pub fn clock_get_hz(&self) -> u64 {
        clock_period_to_hz(self.clock_get())
    }

    /// 设置时钟周期
    #[inline]
    fn set_period(&self, period: u64) {
        self.period.store(period, Ordering::Relaxed);
    }
}

struct RootHwClock;

impl ObjectId for RootHwClock {
    fn compatible(&self) -> &'static str {
        "root_clock"
    }

    fn id(&self) -> &str {
        "root_clock"
    }
}

impl ClkEventImpl for RootHwClock {
    fn notify_clock_upadte(&self, _: ClkEvent) {}
}

static ROOT_CLOCK: LazyLock<Arc<HwClock>> = LazyLock::new(|| {
    let root = Arc::new(
        HwClockBuilder::builder()
            .set_mul_div(1, 1)
            .clock_input(Arc::new(HwClockInput::default()))
            .clock_event(Arc::new(RootHwClock))
            .build(),
    );

    root.clock_set(1);

    root
});

/// 全局根时钟
pub fn root_clock() -> Arc<HwClock> {
    ROOT_CLOCK.clone()
}
